package com.steven.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.concurrent.CompletableFuture;

public class ServerConnectExample {
	private static final int BUF_SIZE = 1024;
	private static final int PORT = 8080;
	private static final int TIMEOUT = 3000;// 1秒
	
	public static void selector() {
		Selector selector = null;
		ServerSocketChannel ssc = null;
		
		try {
			// 1. 打开一个多路复用选择器
			selector = Selector.open();

			// 2. 通道与socket关联
			ssc = ServerSocketChannel.open();	// 打开一个服务端socket nio通道
			ssc.socket().bind(new InetSocketAddress(PORT));	// 该通道绑定端口,监听该端口的上的请求
			// 设置成非阻塞的, 在非阻塞模式下, accept()会立刻返回
			// 如果没有新近来的连接, 返回的将是null, 因此要检查返回的SocketChannel是否是null
			ssc.configureBlocking(false);
			// 注册（register）到想要监控的信道上
			
			
			// 3. 在多路复用选择器上注册通道，注册监听事件
			ssc.register(selector, SelectionKey.OP_ACCEPT);	// 监听Accept事件
			
			// 监听新进来的连接
			while (true) {
				if (selector.select(TIMEOUT) == 0) {
					System.out.println("==");
					continue;
				}

				Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
				while (iter.hasNext()) {
					SelectionKey key = iter.next();
					if (key.isAcceptable()) {
						handleAccept(key);
					}
					if (key.isReadable()) {
						handleRead(key);
					}
					if (key.isWritable() && key.isValid()) {
						handleWrite(key);
					}
					if (key.isConnectable()) {
						System.out.println("isConnectable = true");
					}
					iter.remove();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (selector != null) {
					selector.close();
				}
				if (ssc != null) {
					ssc.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 处理读取事件
	 * @param key
	 * @throws IOException
	 */
	public static void handleRead(SelectionKey key) throws IOException {
		SocketChannel sc = (SocketChannel)key.channel();
		ByteBuffer buf = (ByteBuffer)key.attachment();
		long byteRead = sc.read(buf);
		while (byteRead > 0) {
			buf.flip();
			while (buf.hasRemaining()) {
				System.out.println((char)buf.get());
			}
			System.out.println();
			buf.clear();
			byteRead = sc.read(buf);
		}
		if (byteRead == -1) {
			sc.close();
		}
	}

	/**
	 * 处理写事件
	 * @param key
	 * @throws IOException
	 */
	public static void handleWrite(SelectionKey key) throws IOException {
		ByteBuffer buf = (ByteBuffer)key.attachment();
		buf.flip();
		SocketChannel sc = (SocketChannel)key.channel();
		while (buf.hasRemaining()) {
			sc.write(buf);
		}
		buf.compact();
	}
	public static void handleAccept(SelectionKey key) throws IOException {
		ServerSocketChannel ssChannel = (ServerSocketChannel)key.channel();
		SocketChannel sc = ssChannel.accept();
		sc.configureBlocking(false);
		sc.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocateDirect(BUF_SIZE));	// 注册监听Read事件
	}
	public static void main(String[] args) {
		selector();
	}
}
